package org.redr.canvas;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.redr.widgetGUI.settings.WidgetSettings;

/* 
 * The main handler of objects in Red-R.  All widgets should be accessible from this interface
 */

public class RedRObjects {
	
	private static int widgetNumber = 0;
	private static int iconNumber = 0;
	private static HashMap<String, RRW> 				widgets 	= 	new HashMap<String, RRW>();
	private static HashMap<String, RRWIcon> 			icons 		= 	new HashMap<String, RRWIcon>();
	private static HashMap<String, SchemaView> 			tabs 		= 	new HashMap<String, SchemaView>();
	private static HashMap<String, WidgetRegistry>		registry;
	private static HashMap<String, TemplateRegistry> 	templates;
	private static List<LinkPair>						links		=	new ArrayList<LinkPair>();
	private static HashMap<String, Collection<CanvasLine>>	tabLines	=	new HashMap<String, Collection<CanvasLine>>();
	private static SchemaView activeTab;
	
	public static List<LinkPair> links(){ return links; }
	
	public static void addTab(String name, SchemaView tab){
		tabs.put(name, tab);
		tabLines.put(name, new Vector<CanvasLine>());
		setActiveTab(tab);
	}
	
	public static void setTab(String name){
		setTab(tabs.get(name));
	}
	
	public static void setTab(SchemaView tab){
		setActiveTab(tab);
	}
	
	public static RRW addWidgetByWidgetName(String widgetName) throws Exception
	{
		String wid = widgetName + "_" + widgetNumber++;
		WidgetSettings settings = RedRApplication.settings.getSettings(wid);
		settings.setSetting("caption", wid);
		settings.setSetting("widgetId", wid);
		settings.setSetting("iconId", wid + "+" + iconNumber++);
		return addWidgetByWidgetName(widgetName, settings);
	}
	
	public static RRW addWidgetByWidgetName(String widgetName, WidgetSettings settings) throws Exception
	{
		//make a new widget and add to the widgets hashmap
		try {
			System.out.println("Running Class forName");
			@SuppressWarnings("unchecked")
			Class<RRW> tc = (Class<RRW>) Class.forName(widgetName);

			System.out.println("Class forName done.");
			Constructor<RRW> ct = tc.getConstructor(new Class[]{WidgetSettings.class});
			System.out.println("Done building constructor");
			
			// initialize the new widget
			RRW newWidget = ct.newInstance(settings);
			newWidget.gui.show();
			widgets.put(settings.getSetting("widgetId"), newWidget);
			
			// add the canvas icon
			WidgetRegistry info = registry.get(widgetName);
			RRWIcon icon = new RRWIcon(getActiveTab().scene(), getActiveTab(), info, settings.getSetting("widgetId"));
			settings.setDefault("x", "-1");
			settings.setDefault("y", "-1");
			resolveCollisions(icon, Integer.parseInt(settings.getSetting("x")), Integer.parseInt(settings.getSetting("y")));
			icons.put(settings.getSetting("iconId"), icon);
			getActiveTab().update();
			return newWidget;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new Exception("Failure to initialize widget.");
		}
		//make a new widget icon and add it to icons in the correct canvas (current canvas)
	}

	public static void resolveCollisions(RRWIcon icon, int x, int y) {
//		double bottom = icon.rect().bottom();
//		double top = icon.rect().top();
//		double left = icon.rect().left();
//		double right = icon.rect().right();
//		for (RRWIcon i : widgetIcons()){
//			if (i.equals(icon)) continue;
		if (x == -1 || y == -1){
			x = 30;
			y = 50;
		}
		
		icon.setCoords(x, y);
		boolean invalidPosition = activeTab.getItemsAtPos(icon, RRWIcon.class).size() > 0;
		while (invalidPosition){
			icon.setCoords(x += 100, y);
			invalidPosition = activeTab.getItemsAtPos(icon, RRWIcon.class).size() > 0;
		}
		
	}

	public static Collection<RRW> widgets() {
		return widgets.values();
	}

	public static RRW getWidgetInstanceByID(String widgetID) {
		return widgets.get(widgetID);
	}

	public static Collection<RRWIcon> widgetIcons() {
		return icons.values();
	}
	
	public static void removeWidget(String id){
		RRW thisWidget = widgets.get(id);
		thisWidget.deleteWidget();
		widgets.remove(id);
	}
	
	public static void removeWidget(RRWIcon icon){
		removeWidget(icon.instance().widgetID());
	}
	
	public static void registerWidgets(){
		// locate the widget XML files and store these as a Vector<WidgetRegistry> structure
		// Vector<WidgetRegistry> xmlRegistry = getXMLRegistry();
		
		System.out.println("Registering widgets");
		registry	= new HashMap<String, WidgetRegistry>();
		List<String> ignores = new ArrayList<String>();
		ignores.add("org.redr*");
		ignores.add("com.trolltech*");
		ignores.add("plugins.*");
		ClassFinder widgetFinder = new ClassFinder(ignores);
		Vector<Class<?>> widgetClasses = widgetFinder.findSubclasses("org.redr.canvas.RRW");
		for (Class<?> c : widgetClasses){
			try{
				WidgetRegistry wr = new WidgetRegistry(c);
				wr.forName = c.getName();
				registry.put(c.getName(), wr);
			} catch (Exception e){
				e.printStackTrace();
			}
		}
		/*
		Class<?> asClass = this.getClass();
		for (Field m : asClass.getFields()){
			try {
				System.out.println(String.format("%s=%s", 
						m.getName(), 
						m.get(null).toString()));
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		 }
		 */
	}
	
	public static void registerTemplates(){
		templates = new HashMap<String, TemplateRegistry>();
	}

	public static Map<String, WidgetRegistry> registry() {
		// TODO Auto-generated method stub
		return registry;
	}

	public static Map<String, TemplateRegistry> templates() {
		// TODO Auto-generated method stub
		return templates;
	}

	public static void closeAllWidgets() {
		// TODO Auto-generated method stub
		System.out.println("Closing Widgets");
		for (RRW w : widgets.values()){
			w.closeWidget();
		}
	}

	public static void removeLineInstance(CanvasLine line) {
		// TODO Auto-generated method stub
		
	}

	public static Collection<RRWIcon> getIconsByTab(String name) {
		// TODO Auto-generated method stub
		return icons.values();
	}

	public static void addLine(RRWIcon start, RRWIcon end) {
		// Add line on the canvas
		System.out.println("Adding canvas connection");
		
		// get the compatible signals
		Collection<OutputSocket>	outputs = 	start.instance().outputs().values();
		Collection<InputSocket>		inputs	=	end.instance().inputs().values();
		
		System.out.println("Checking compatible signals.");
		List<String[]> matchInputs = new ArrayList<String[]>();
		for (OutputSocket o : outputs){
			for (String os : o.connectionTypes()){
				for (InputSocket i : inputs){
					if ( i.connectionTypes().contains(os) ){
						matchInputs.add(new String[] {o.getSignalId(), i.getSignalId()});
						System.out.println("Found matching signal type " + os);
						continue;
					}
				}
			}
		}
		System.out.println(String.format("Number of matching connections: %s", matchInputs.size()));
		if (matchInputs.size() < 1) return;
		else if (matchInputs.size() == 1){
			System.out.println("Connecting signals.");
			connectSignal(start.instance(), end.instance(), matchInputs.get(0)[0], matchInputs.get(0)[1]);
			connectLine(start, end);
		} else {
			System.out.println("Signal connection ambiguous, adding signal dialog in later version.");
		}
		
	}

	private static void connectLine(RRWIcon inWidget, RRWIcon outWidget) {
		// Add a permanent canvas line to the view.
		// check if the line exists already
		for (CanvasLine l : linesByTab(getActiveTab().name)){
			if (l.inWidget().equals(inWidget) & l.outWidget().equals(outWidget)) {
				System.out.println("Line already exists, not adding line.");
				return;
			}
		}
		System.out.println("Adding line to canvas.");
		CanvasLine newLine	=	new CanvasLine(getActiveTab(), inWidget, outWidget);
		tabLines.get(getActiveTab().name).add(newLine);
		
	}

	private static Collection<CanvasLine> linesByTab(String tabName) {
		return tabLines.get(tabName);
	}

	private static void connectSignal(RRW output, RRW input,
			String outSocketId, String inSocketId) {
		// check if we need to remove any signals
		if (!(input.inputsGet(inSocketId).isMultiple())){
			// TODO add code to remove connected signals.
		}
		System.out.println("Connecting input and output sockets.");
		output.outputs().get(outSocketId).connectInput(input.inputsGet(inSocketId));
	}

	public static SchemaView getActiveTab() {
		return activeTab;
	}

	public static void setActiveTab(SchemaView activeTab) {
		RedRObjects.activeTab = activeTab;
	}
}
